package com.bawei.es.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.bawei.common.core.domain.R;
import com.bawei.common.core.exception.ServiceException;
import com.bawei.common.core.utils.StringUtils;
import com.bawei.es.service.EsDocService;
import com.bawei.es.service.EsIndexService;
import com.bawei.system.domain.utils.SearchResultUtils;
import com.bawei.system.domain.vo.EsDocInsertVo;
import com.bawei.system.domain.vo.EsDocQueryVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.search.TotalHits;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.client.indices.GetIndexResponse;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MultiMatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.suggest.Suggest;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.SuggestBuilders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Service
@Slf4j
public class EsDocServiceImpl implements EsDocService {

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Autowired
    private EsIndexService esIndexService;

    @Value("classpath:mappings/product_info.json")
    private Resource jsonResource;

    @Override
    public R<Boolean> batchInsertDocs(List<EsDocInsertVo> esDocInsertVo) {
        BulkRequest request = new BulkRequest();
        esDocInsertVo.forEach(e -> {
            IndexRequest source = new IndexRequest(e.getIndexName()).source(e.getData(), XContentType.JSON);
            if (StringUtils.isNotEmpty(e.getId())) {
                source.id(e.getId());
            }
            request.add(source);
        });
        try {
            restHighLevelClient.bulk(request, RequestOptions.DEFAULT);
            return R.ok(true, "插入成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
        return R.fail(false, "插入失败");
    }

    @Override
    public R<List<Map>> queryDocs(EsDocQueryVo searchReq) {
        if (StringUtils.isEmpty(searchReq.getKeyWords())){
            throw new ServiceException("搜索参数不可以为空");
        }
        SearchRequest searchRequest = new SearchRequest(searchReq.getIndexName());
        SearchSourceBuilder searchSourceBuilder = SearchSourceBuilder.searchSource();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        List<String> fields = searchReq.getSearchFields();
        String[] fieldName = new String[fields.size()];
        // 商品名称搜索
        for (int i = 0; i < fields.size(); i++) {
            String s = fields.get(i);
            fieldName[i]=s;
        }
        MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery(searchReq.getKeyWords(), fieldName);
        boolQueryBuilder.must(multiMatchQueryBuilder);

        // 分页
        searchSourceBuilder.from((searchReq.getPageVo().getCurrentPage() - 1) * searchReq.getPageVo().getPageSize());
        searchSourceBuilder.size(searchReq.getPageVo().getPageSize());

        searchSourceBuilder.query(boolQueryBuilder);

        List<String> highLightFields = searchReq.getHighLightFields();
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        if(CollectionUtils.isEmpty(highLightFields)){
            highLightFields = searchReq.getSearchFields();
        }
        for (String high : highLightFields) {
            highlightBuilder.field(high);
            highlightBuilder.preTags("<span style='color:red'>");
            highlightBuilder.postTags("</span>");
            searchSourceBuilder.highlighter(highlightBuilder);
        }
        searchRequest.source(searchSourceBuilder);
        try {
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            SearchHits searchResponseHits = searchResponse.getHits();
            TotalHits totalHits = searchResponseHits.getTotalHits();
            // 若未查询到数量结果，直接进行返回
            if (totalHits.value != 0){
                SearchResultUtils.setTotal(totalHits.value);
            }
            List<Map> productInfoResponseList = new ArrayList<>();
            SearchHit[] searchHits = searchResponseHits.getHits();
            for (SearchHit searchHit : searchHits) {
                // 源数据
                String sourceAsString = searchHit.getSourceAsString();
                Map dataMap =
                        JSONObject.parseObject(sourceAsString, Map.class);
                // 高亮
                Map<String, HighlightField> highlightFields =
                        searchHit.getHighlightFields();
                if (highlightFields != null){
                    for (String field : fields) {
                        HighlightField highlightField = highlightFields.get(field);
                        if (highlightField != null){
                            // 拼装
                            Text[] fragments = highlightField.getFragments();
                            StringBuilder sb = new StringBuilder();
                            for (Text fragment : fragments) {
                                sb.append(fragment);
                            }
                            dataMap.put(field,sb.toString());
                        }
                    }
                }
                productInfoResponseList.add(dataMap);
            }
            return R.ok(productInfoResponseList);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public R querySuggestions(String indexName,String keyWord,String suggestFiled) {

        if (!esIndexService.indexExit(indexName).getData()){
            throw new RuntimeException("索引不存在");
        }
        SearchRequest request = new SearchRequest(indexName);
        request.source().suggest(new SuggestBuilder().addSuggestion(
                "title_suggest",
                SuggestBuilders
                        .completionSuggestion(suggestFiled)
                        .prefix(keyWord)
                        .skipDuplicates(true)
                        .size(10)
        ));
        SearchResponse response = null;
        try {
            response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        Suggest suggest = response.getSuggest();
        List<String> list = new ArrayList<>();
        suggest.getSuggestion("title_suggest").forEach(suggestion -> {
            suggestion.getOptions().forEach(entry -> {
                list.add(entry.getText().string());
            });
        });
        return R.ok(list);

    }

    @Override
    public R batchDeleteDocs(ArrayList<String> list, String indexName) {
        try {
            for (String id : list) {
                DeleteRequest request = new DeleteRequest(indexName, id);
                restHighLevelClient.delete(request, RequestOptions.DEFAULT);
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        return null;
    }

    @Override
    public List<String> queryDocsIndices() {
        try {
            // 创建获取所有索引的请求
            GetIndexRequest request = new GetIndexRequest("*");
            // 执行请求并获取响应
            GetIndexResponse response = restHighLevelClient.indices().get(request, RequestOptions.DEFAULT);

            String[] indices = response.getIndices();
            // 输出所有索引的名称
            List<String> indexList = new ArrayList<>();
            for (String index : indices) {
                indexList.add(index);
            }
            return indexList;
        } catch (IOException e) {
            throw new RuntimeException("查询所有索引失败！");
        }
    }
}
